/* Includes ------------------------------------------------------------------*/
#include "bmp280.h"
#include "gpio.h"

/*----------------------------------------------------------------------------*/
/* Configure bmp280                                                             */
/*----------------------------------------------------------------------------*/

uint8_t bmp_slave_addr = 0xec;

uint16_t dig_T1, dig_P1;
int16_t dig_T2, dig_T3, dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9;

float t_fine;

void BMP280_sda_out(uint8_t out) {
  GPIO_InitTypeDef GPIO_InitStruct;

  HAL_GPIO_WritePin(bmp_GPIO_Port, bmp_sda_Pin, GPIO_PIN_SET);

  GPIO_InitStruct.Pin = bmp_sda_Pin;
  if (out) {
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  } else {
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  }

  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(bmp_GPIO_Port, &GPIO_InitStruct);
}

void BMP280_IIC_Start(void) {
  BMP280_SCL_Set();
  BMP280_SDA_Set();
  BMP280_SDA_Clr();
}

void BMP280_IIC_Stop(void) {
  BMP280_SCL_Set();
  BMP280_SDA_Clr();
  BMP280_SDA_Set();
}

void BMP280_IIC_Wait_Ack(void) {
  BMP280_SCL_Set();
  BMP280_SCL_Clr();
}

void BMP280_IIC_Ack(uint8_t ack) {
  BMP280_SCL_Clr();

  if (ack) {
    BMP280_SDA_Clr();
  } else {
    BMP280_SDA_Set();
  }

  BMP280_SCL_Set();
  BMP280_SCL_Clr();
}

void BMP280_Write_IIC_Byte(uint8_t IIC_Byte) {
  uint8_t i;

  BMP280_SCL_Clr();

  for (i = 0; i < 8; i++) {
    if (IIC_Byte & 0x80) {
      BMP280_SDA_Set();
    } else {
      BMP280_SDA_Clr();
    }
    IIC_Byte <<= 1;
    BMP280_SCL_Set();
    BMP280_SCL_Clr();
  }
}

uint8_t BMP280_Read_IIC_Byte(uint8_t ack) {
  uint8_t i, receive = 0;

  BMP280_sda_out(0);

  for (i = 0; i < 8; i++) {
    BMP280_SCL_Clr();
    __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
    __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
    BMP280_SCL_Set();

    receive <<= 1;

    if (BMP280_READ_SDA) {
      receive++;
    }
  }

  BMP280_sda_out(1);

  if (ack) {
    BMP280_IIC_Ack(1);
  } else {
    BMP280_IIC_Ack(0);
  }

  return receive;
}

void BMP280_Single_Write(uint8_t addr, uint8_t data) {
  BMP280_IIC_Start();
  BMP280_Write_IIC_Byte(bmp_slave_addr);
  BMP280_IIC_Wait_Ack();
  BMP280_Write_IIC_Byte(addr);
  BMP280_IIC_Wait_Ack();
  BMP280_Write_IIC_Byte(data);
  BMP280_IIC_Wait_Ack();
  BMP280_IIC_Stop();
}

uint8_t BMP280_Single_Read(uint8_t addr) {
  uint8_t rec;

  BMP280_IIC_Start();
  BMP280_Write_IIC_Byte(bmp_slave_addr);
  BMP280_IIC_Wait_Ack();
  BMP280_Write_IIC_Byte(addr);
  BMP280_IIC_Wait_Ack();

  BMP280_IIC_Start();
  BMP280_Write_IIC_Byte(bmp_slave_addr + 1);
  BMP280_IIC_Wait_Ack();

  rec = BMP280_Read_IIC_Byte(0);
  BMP280_IIC_Stop();

  return rec;
}

void BMP280_Ready() {
  dig_T1 = BMP280_Single_Read(0x89);
  dig_T1 <<= 8;
  dig_T1 |= BMP280_Single_Read(0x88);

  dig_T2 = BMP280_Single_Read(0x8b);
  dig_T2 <<= 8;
  dig_T2 |= BMP280_Single_Read(0x8a);

  dig_T3 = BMP280_Single_Read(0x8d);
  dig_T3 <<= 8;
  dig_T3 |= BMP280_Single_Read(0x8c);

  dig_P1 = BMP280_Single_Read(0x8f);
  dig_P1 <<= 8;
  dig_P1 |= BMP280_Single_Read(0x8e);

  dig_P2 = BMP280_Single_Read(0x91);
  dig_P2 <<= 8;
  dig_P2 |= BMP280_Single_Read(0x90);

  dig_P3 = BMP280_Single_Read(0x93);
  dig_P3 <<= 8;
  dig_P3 |= BMP280_Single_Read(0x92);

  dig_P4 = BMP280_Single_Read(0x95);
  dig_P4 <<= 8;
  dig_P4 |= BMP280_Single_Read(0x94);

  dig_P5 = BMP280_Single_Read(0x97);
  dig_P5 <<= 8;
  dig_P5 |= BMP280_Single_Read(0x96);

  dig_P6 = BMP280_Single_Read(0x99);
  dig_P6 <<= 8;
  dig_P6 |= BMP280_Single_Read(0x98);

  dig_P7 = BMP280_Single_Read(0x9b);
  dig_P7 <<= 8;
  dig_P7 |= BMP280_Single_Read(0x9a);

  dig_P8 = BMP280_Single_Read(0x9d);
  dig_P8 <<= 8;
  dig_P8 |= BMP280_Single_Read(0x9c);

  dig_P9 = BMP280_Single_Read(0x9f);
  dig_P9 <<= 8;
  dig_P9 |= BMP280_Single_Read(0x9e);
}

int16_t BMP280_Temp(void) {
  uint32_t adc_T;
  float var1, var2;

  adc_T = BMP280_Single_Read(0xfa);
  adc_T <<= 8;
  adc_T |= BMP280_Single_Read(0xfb);
  adc_T <<= 8;
  adc_T |= BMP280_Single_Read(0xfc);
  adc_T >>= 4;

  BMP280_Ready();

  var1 = (((double) adc_T) / 16348.0 - ((double)dig_T1) / 1024.0) * ((double) dig_T2);
  var2 = ((((double)adc_T) / 131072.0 - ((double)dig_T1) / 8192.0) * (((double) adc_T) / 131072.0 - ((double)dig_T1) / 8192.0)) * ((double)dig_T3);
  t_fine = var1 + var2;

  return (int16_t)((var1 + var2) / 51.2);
}

int32_t BMP280_Pressure(void) {
  uint32_t adc_P;
  float var1, var2, p;

  adc_P = BMP280_Single_Read(0xf7);
  adc_P <<= 8;
  adc_P |= BMP280_Single_Read(0xf8);
  adc_P <<= 8;
  adc_P |= BMP280_Single_Read(0xf9);
  adc_P >>= 4;

  var1 = ((double)t_fine / 2.0) - 64000.0;
  var2 = var1 * var1 * ((double)dig_P6) / 32768.0;
  var2 = var2 + var1 * ((double)dig_P5) * 2.0;
  var2 = (var2 / 4.0) + (((double)dig_P4) * 65536.0);
  var1 = (((double)dig_P3) * var1 * var1 / 524288.0 + ((double)dig_P2) * var1) / 524288.0;
  var1 = (1.0 + var1 / 32768.0) * ((double)dig_P1);
  p = 1048576.0 - (double)adc_P;
  p = (p - (var2 / 4096.0)) * 6250.0 / var1;
  var1 = ((double)dig_P9) * p * p / 2147483648.0;
  var2 = p * ((double)dig_P8) / 32768.0;
  p = p + (var1 + var2 + ((double)dig_P7)) / 16.0;

  return p;
}

void BMP280_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(bmp_GPIO_Port, bmp_scl_Pin | bmp_sda_Pin, GPIO_PIN_SET);

  /*Configure GPIO pins : PBPin PBPin */
  GPIO_InitStruct.Pin = bmp_scl_Pin | bmp_sda_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(bmp_GPIO_Port, &GPIO_InitStruct);

  // [01010111] 010=Tx2, 101=Px16, 11=Normal Mode
  BMP280_Single_Write(0xf4, 0x57);
  // [111111x0] 111=Standby4000ms, 111=IIRx16, x0=Dis-SPI3
  BMP280_Single_Write(0xf5, 0xfc);
}
